<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * This file contains several helper functions for strings. The following
 * functions are defined:
 *
 * - lcfirst
 * - str_rand
 * - camel2uscore
 * - uscore2camel
 * - hex2rgb
 * - truncate_string
 * - prep_url
 * - encode_html
 * - encode_php
 * - validate_ip
 * - random_password
 *
 * PHP version 5
 *
 * This file is part of Majic - A rapid-development web application framework
 * for PHP 5.
 *
 * Majic is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * Majic is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Majic. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package     Majic
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @copyright   2007-2008 Michael J. I. Jackson
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
 * @version     SVN: $Id: string.php 34 2008-07-19 04:50:53Z mjijackson $
 */

/**
 * Converts the first character in a string to lowercase.
 *
 * @param   string  $str        The string to convert
 * @return  string              The string with the first letter lowercased
 */
if (!function_exists('lcfirst')) {
    function lcfirst($str)
    {
        return strtolower(substr($str, 0, 1)) . substr($str, 1);
    }
}

/**
 * A complement to PHP's native array_rand(), this function will return a random
 * character from the given string.
 *
 * @param   string  $str        The string to select from
 * @return  string              A random character
 */
function str_rand($str)
{
    return $str[mt_rand(0, strlen($str) - 1)];
}

/**
 * Converts a camel cased string to an underscore separated one. Example:
 * someCamelCasedString => some_camel_cased_string
 *
 * @param   string  $camel      The string to convert
 * @return  string              The string separated with underscores
 */
function camel2uscore($str)
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', '\1_\2', $str));
}

/**
 * Converts an underscore separated string to a camel cased one. Example:
 * some_under_scored_string => someUnderScoredString
 *
 * @param   string  $underscore     The string to convert
 * @return  string                  The string camel-cased
 */
function uscore2camel($str)
{
    return lcfirst(preg_replace('/(_[a-z])/e', "strtoupper('\\1')", $str));
}

/**
 * Converts a hexadecimal string to an RGB array. If the $string parameter is
 * true, the return value will be an RGB formatted string that can be used in
 * CSS declarations (i.e. rgb(123,32,54)).
 *
 * @param   string  $hex        The color in hexadecimal format
 * @param   bool    $string     True to return the RGB value as a string
 * @return  array               An array with RGB values
 * @throws  Exception           If the $hex string is not 3 or 6 characters in
 *                              length
 */
function hex2rgb($hex, $string = false)
{
    $hex = str_replace('#', '', $hex);
    $rgb = array();

    switch (strlen($hex)) {
        case 3:
            $hex = str_split($hex, 1);
            $rgb['r'] = hexdec($hex[0] . $hex[0]);
            $rgb['g'] = hexdec($hex[1] . $hex[1]);
            $rgb['b'] = hexdec($hex[2] . $hex[2]);
        break;
        case 6:
            $hex = str_split($hex, 2);
            $rgb['r'] = hexdec($hex[0]);
            $rgb['g'] = hexdec($hex[1]);
            $rgb['b'] = hexdec($hex[2]);
        break;
        default:
            throw new Exception("Invalid length: $hex");
    }

    return $string ? 'rgb(' . implode(',', $rgb) . ')' : $rgb;
}

/**
 * Truncates the given string to the desired length and replaces the last
 * characters with the trailing string.
 *
 * @param   string  $string     The string to truncate
 * @param   int     $length     The length to truncate the string to
 * @param   string  $trail      The trailing string
 * @return  string
 * @access  public
 */
function truncate_string($string, $length = 30, $trail = '...')
{
    if (strlen($string) > $length) {
        $string = substr($string, 0, $length - strlen($trail));
        return $string . $trail;
    }
    return $string;
}

/**
 * Prepends an http:// protocol string if the string doesn't already have
 * one.
 *
 * @param   string  $value      The URL to prep
 * @return  string              The URL with http:// prepended
 */
function prep_url($value)
{
    if (!preg_match('/^https?:\/\//', $value)) {
        $value = "http://$value";
    }
    return $value;
}

/**
 * Encodes HTML to be safely used in an HTML document.
 *
 * @param   string  $value      The HTML string to prep
 * @return  string              The HTML with all ', ", <, and > characters
 *                              converted to their HTML equivalents
 */
function encode_html($value)
{
    return str_replace(
        array("'", '"', '<', '>'),
        array("&#39;", "&quot;", '&lt;', '&gt;'),
        stripslashes($value));
}

/**
 * Encodes PHP tags in a string.
 *
 * @param   string  $value      The string to encode
 * @return  string              The string with PHP tags encoded properly
 */
function encode_php($value)
{
    return preg_replace(array('/<\?(php)?/i', '/\?>/'), array('&lt;?\1', '?&gt;'), $value);
}

/**
 * Validates an IP address. Checks to see that the address:
 *
 * - has 4 segments
 * - the first segment is not 0
 * - each segment is comprised of 3 numbers that are not greater than 255
 *
 * @param   string  $ip         The address to validate
 * @return  bool                True if the address validates, false otherwise
 */
function validate_ip($ip)
{
    $ip_segments = explode('.', $ip);

    if (count($ip_segments) != 4 || substr($ip_segments[0], 0, 1) == '0') {
        return false;
    }
    foreach ($ip_segments as $s) {
        if (preg_match("/[^0-9]/", $s) || $s > 255 || strlen($s) > 3) {
            return false;
        }
    }

    return true;
}

/**
 * Generates a random password. Note: This function may return combinations of
 * characters that must be escaped in order to display properly in an HTML
 * document. Also, all passwords generated with this function will always
 * start with an alphabetic character.
 *
 * @param   int     $length             The desired password length
 * @param   bool    $allow_specials     True to allow special characters
 * @param   bool    $increase_entropy   True to increase entropy in the string
 *                                      by substituting non-alpha characters
 *                                      when a random maximum has been met
 * @return  string                      A random password string
 */
function random_password($length = 9, $allow_specials = true,
    $increase_entropy = false)
{
    static $alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    static $num = '1234567890';
    static $spec = '!@#$%^&*(){}[];:.,?';

    $avail = $alpha . $num;
    $non_alpha = $num;
    if ($allow_specials) {
        $avail .= $spec;
        $non_alpha .= $spec;
    }

    $count_alpha = 0;
    $password = str_rand($alpha);

    for ($i = 0; $i < $length - 1; $i++) {
        $c = str_rand($avail);

        if ($increase_entropy && ctype_alpha($c)) {
            $count_alpha++;
            if ($count_alpha > mt_rand(0, $length)) {
                $c = str_rand($non_alpha);
            }
        }

        $password .= $c;
    }

    return $password;
}

?>
